#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
#include "../MyTcp2/tcpsession.h"
#include "../MyTcp2/pack.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent), ui(new Ui::Widget),settings(new QSettings(":/config/config.ini" , QSettings::IniFormat))
{
    ui->setupUi(this);
    this->setWindowTitle("服务器");
    //tcp服务器
    server = new QTcpServer(this);
    QString host=settings->value("network/host").toString();
    int port=settings->value("network/port").toInt();
    server->listen(QHostAddress(host), port);
    QObject::connect(server, &QTcpServer::newConnection, this, &Widget::onNewConnection);
    //数据库
    db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("server.db");
    if(!db.open()){
        qDebug()<<"Widget::Widget   数据库打开失败！！！";
        return;
    }
    //dao
    user_dao = new tbl_user_dao(db, this);
    house_dao = new tbl_house_dao(db ,this);
}

Widget::~Widget()
{
    db.close();
    delete ui;
}

void Widget::onNewConnection()
{
    QTcpSocket* socket=server->nextPendingConnection();
    //
    TcpSession* session=new TcpSession(socket, this);
    QObject::connect(session, &TcpSession::readyRead, this, &Widget::onReadyRead);
    QObject::connect(session, &TcpSession::disconnected, this, &Widget::onDisconnected);
}

void Widget::onReadyRead()
{
    TcpSession* session=dynamic_cast<TcpSession*>(sender());
    session->readPacks();
    int vectorSize=session->getVectorSize();
    for(int i=0;i<vectorSize;i++){
        //每次循环就是一个完整的数据包
        Pack pack=session->popFrontPack();
        Pack sendPack;//返回报文的包
        //业务处理
        //若返回false，则有handler给客户端发送报文，不走统一回复流程
        if(handle(session, pack, sendPack)){
            session->send((char*)&sendPack, sendPack.getPacksize());//回复报文(统一回复)
        }
        continue;
    }
}

void Widget::onDisconnected()
{
    TcpSession* session=dynamic_cast<TcpSession*>(sender());
    QString username=session->getValue("username");
    if(username == "")
        return;
    //userMap
    userMap.remove(username);

    //删除该连接所在的直播区域(通过用户名，找所在的直播房间)
    for(auto & house: houseMap){
        if(!house.contains(username))
            continue;//该用户不在这个房间
        //该用户在这个房间
        house.remove(session->getValue("username"));

        //通知这个房间的其他成员：（1）成员列表 （2）有人退出了房间
        //成员列表信息
        Pack pack2;
        pack2.setType(HOUSE_MEMS);
        pack2.setFrame(CALL);
        for(auto & e: house){
            pack2.append(e->getValue("nickname"));
        }

        //退出消息
        Pack pack3;
        pack3.setType(HOUSE_MSG);
        pack3.setFrame(CALL);
        pack3.append("["+session->getValue("nickname")+"]退出了直播间");

        //
        Pack pack4(HOUSE_LIST, CALL);
        handleHouseList(pack4);

        //向该直播间的其他用户推送消息
        for(auto & e: house){
            e->send((char*)&pack2, pack2.getPacksize());
            e->send((char*)&pack3, pack3.getPacksize());
        }
        for(auto & e: userMap){
            e->send((char*)&pack4, pack4.getPacksize());
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////

//返回true代表要回复报文， false代表不要回复报文
bool Widget::handle(TcpSession* session,Pack &rcvPack, Pack &sendPack)
{
    sendPack.setType(rcvPack.getType());
    sendPack.setFrame(RET);

    switch(rcvPack.getType()){
    case LOGIN:
        return handleLogin(session,rcvPack, sendPack);
    case REGIST:
        return handleReg(rcvPack, sendPack);
    case LOGOUT:
        return handleLogout(rcvPack, sendPack);
    case HOUSE_ADD:
        return handleHouseAdd(rcvPack, sendPack);
    case HOUSE_LIST:
        return handleHouseList(sendPack);
    case HOUSE_DELETE:
        return handleHouseDel(rcvPack, sendPack);
    case HOUSE_UPDATE:
        return handleHouseUpdate(rcvPack, sendPack);
    case HOUSE_ENTER:
        return handleHouseEnter(session, rcvPack, sendPack);
    case HOUSE_EXIT:
        return handleHouseExit(session, rcvPack);
    case BULLET:
        return handleHouseBullet(session, rcvPack);
    case GIFT:
        return handleHouseGift(session, rcvPack);
    case VIDEO:
        return handleHouseVideo(session, rcvPack);
    case CAMERA:
        return handleHouseCamera(session, rcvPack);
    default:
        return false;
    }
}

bool Widget::handleLogin(TcpSession* newSession,Pack& rcvPack, Pack& sendPack){
    QString msg;//接收业务处理的日志信息
    QString username=rcvPack.getFieldList()[0];
    QString passwd=rcvPack.getFieldList()[1];
    //
    if(!user_dao->select(username, passwd, msg)){
        sendPack.append("FAIL");
        sendPack.append(msg);
        return true;
    }
    // 用户名密码校验通过
    if(userMap.contains(username)){
        TcpSession* oldSession = userMap.find(username).value();
        //向客户端发送登出报文
        Pack pack(LOGOUT, CALL);
        pack.append("用户已经在其他终端登录，您即将被强制退出");
        //通知旧的客户端强制下线
        oldSession->send((char*)&pack, pack.getPacksize());
        oldSession->clearSessionInfo();
        userMap.remove(username);
    }
    //服务端保存session信息
    newSession->putValue("username", username);
    newSession->putValue("nickname", msg);
    userMap.insert(username, newSession);

    //组装登录成功返回报文
    sendPack.append("SUCCESS");
    //成功的话，借助msg传递nickname
    sendPack.append(username);
    sendPack.append(msg);
    return true;
}

bool Widget::handleReg(Pack& rcvPack, Pack& sendPack){
    QString msg;//接收业务处理的日志信息
    QString username=rcvPack.getFieldList()[0];
    QString passwd=rcvPack.getFieldList()[1];
    QString nickname=rcvPack.getFieldList()[2];

    if(user_dao->insert(username, passwd, nickname, msg)){
        sendPack.append("SUCCESS");
    }else{
        sendPack.append("FAIL");
        sendPack.append(msg);
    }
    return true;
}

bool Widget::handleLogout(Pack &rcvPack, Pack &sendPack)
{
    QString username= rcvPack.getFieldList()[0];
    if(userMap.contains(username))
        userMap.remove(username);
    return false;
}

bool Widget::handleHouseAdd(Pack& rcvPack, Pack& sendPack){
    QString msg;//接收业务处理的日志信息
    QString username=rcvPack.getFieldList()[0];
    QString housename=rcvPack.getFieldList()[1];
    QString owner=rcvPack.getFieldList()[2];
    int limit=rcvPack.getFieldList()[3].toInt();
    QString starttime=rcvPack.getFieldList()[4];
    QString endtime=rcvPack.getFieldList()[5];

    if(!house_dao->insertRow(housename,owner,limit,starttime,endtime,msg)){
        //房间创建失败
        sendPack.append("FAIL");
        sendPack.append(msg);
        return true;
    }
    //房间创建成功

    //查询最新的房间列表
    Pack pack4(HOUSE_LIST, CALL);
    handleHouseList(pack4);
    //通知所有在线客户端刷新列表 userMap
    for(auto & e: userMap){
        e->send((char*)&pack4, pack4.getPacksize());
    }

    //给当前客户端返回创建成功消息
    sendPack.append("SUCCESS");
    return true;
}

bool Widget::handleHouseList(Pack& sendPack){
    QString msg;//接收业务处理的日志信息
    //
    QVector<QStringList> table;

    if(!house_dao->selectRows(table, msg)){
        sendPack.append("FAIL");
        sendPack.append(msg);
        return true;
    }

    //查询到数据
    for(int i=0;i<table.size();i++){
        QStringList row=table[i];
        //0-houseid
        int houseid=row[0].toInt();
        if(!houseMap.contains(houseid)){
            QMap<QString, TcpSession*> house;
            houseMap.insert(houseid, house);
        }
        QMap<QString, TcpSession*> & house = houseMap.find(houseid).value();
        int onlineNum=house.size();

        for(int j=0;j<row.size();j++){
            if(j == 3){//limit
                sendPack.append(QString::number(onlineNum)+"/"+row[j]);
                continue;
            }
            sendPack.append(row[j]);
        }
    }
    return true;
}

bool Widget::handleHouseDel(Pack& rcvPack, Pack& sendPack){
    QString msg;//接收业务处理的日志信息
    QString username=rcvPack.getFieldList()[0];
    int serial=rcvPack.getFieldList()[1].toInt();
    if(!house_dao->deleteRow(serial,msg)){
        sendPack.append("FAIL");
        sendPack.append(msg);
        return true;
    }
    sendPack.append("SUCCESS");
    //
    Pack pack4(HOUSE_LIST, CALL);
    handleHouseList(pack4);
    for(auto & e : userMap){
        e->send((char*)&pack4, pack4.getPacksize());
    }
    return true;
}

bool Widget::handleHouseUpdate(Pack& rcvPack, Pack& sendPack){
    QString msg;//接收业务处理的日志信息
    QString username=rcvPack.getFieldList()[0];
    int serial=rcvPack.getFieldList()[1].toInt();
    QString housename=rcvPack.getFieldList()[2];
    QString owner=rcvPack.getFieldList()[3];
    int limit=rcvPack.getFieldList()[4].toInt();
    QString starttime=rcvPack.getFieldList()[5];
    QString endtime=rcvPack.getFieldList()[6];
    if(!house_dao->updateRow(serial,housename,owner,limit,starttime,endtime,msg)){
        sendPack.append("FAIL");
        sendPack.append(msg);
        return true;
    }
    //修改成功
    sendPack.append("SUCCESS");

    Pack pack4(HOUSE_LIST, CALL);
    handleHouseList(pack4);
    for(auto & e : userMap){
        e->send((char*)&pack4, pack4.getPacksize());
    }
    return true;
}

bool Widget::handleHouseEnter(TcpSession* session, Pack& rcvPack, Pack& sendPack){
    QString msg;
    QStringList& fields=rcvPack.getFieldList();
    QString username=fields[0];
    QString nickname=fields[1];
    int houseID=fields[2].toInt();
    QString housename=fields[3];
    //人数是否已满
    int limitPers=house_dao->selectLimit(houseID, msg);
    if(!houseMap.contains(houseID)){
        QMap<QString, TcpSession*> house;//可能会有风险
        houseMap.insert(houseID, house);
    }
    QMap<QString, TcpSession*> & house = houseMap.find(houseID).value();
    int numPers = house.size();
    if(numPers+1 > limitPers){//超员了
        sendPack.append("FAIL");
        sendPack.append("人数已满，无法进入该房间");
        return true;
    }
    sendPack.append("SUCCESS");
    sendPack.append("进入房间成功");
    //保存当前用户的信息
    session->putValue("username", username);
    session->putValue("nickname", nickname);
    session->putValue("houseID", QString::number(houseID));
    session->putValue("housename", housename);
    house.insert(username, session);

    //(1)房间成员列表
    Pack pack2(HOUSE_MEMS, CALL);
    for(auto & e: house){
        pack2.append(e->getValue("nickname"));
    }

    //(2)进入房间消息
    Pack pack3(HOUSE_MSG, CALL);
    pack3.append(nickname+" 进入了直播间");

    //(3)外部房间列表的实时人数信息
    Pack pack4(HOUSE_LIST, CALL);
    handleHouseList(pack4);

    //(3)通知所有的客户端
    for(auto & e: house){
        e->send((char*)&pack2, pack2.getPacksize());
        e->send((char*)&pack3, pack3.getPacksize());
    }
    for(auto & e: userMap){
        e->send((char*)&pack4, pack4.getPacksize());
    }
    return true;//这种类型特殊处理
}

bool Widget::handleHouseExit(TcpSession* session, Pack& rcvPack){
    //退出直播间
    QStringList& fields=rcvPack.getFieldList();
    QString & username=fields[0];
    QString & nickname=fields[1];
    int houseID=fields[2].toInt();
    //
    if(!houseMap.contains(houseID))
        return false;
    //
    QMap<QString, TcpSession*> & house = houseMap.find(houseID).value();
    house.remove(username);
    //

    //(1)房间成员列表
    Pack pack2(HOUSE_MEMS, CALL);
    for(auto & e: house){
        pack2.append(e->getValue("nickname"));
    }

    //(2)进入房间消息
    Pack pack3(HOUSE_MSG, CALL);
    pack3.append(nickname+" 进入了直播间");

    //(4)外部房间列表的实时人数信息
    Pack pack4(HOUSE_LIST, CALL);
    handleHouseList(pack4);

    //(3)通知所有的客户端
    for(auto & e: house){
        e->send((char*)&pack2, pack2.getPacksize());
        e->send((char*)&pack3, pack3.getPacksize());
    }
    for(auto & e: userMap){
        e->send((char*)&pack4, pack4.getPacksize());
    }

    return true;
}

bool Widget::handleHouseBullet(TcpSession* session, Pack& rcvPack){
    return handleHouseGift(session, rcvPack);
}

bool Widget::handleHouseGift(TcpSession* session, Pack& rcvPack){
    //弹幕和礼物的处理方式一样
    //房间号
    QStringList fields=rcvPack.getFieldList();
    int houseID= fields[2].toInt();

    //根据房间号查找所有的session
    if(!houseMap.contains(houseID))
        return false;

    QMap<QString, TcpSession*> & house=houseMap.find(houseID).value();
    for(auto & e: house){
        e->send((char*)&rcvPack, rcvPack.getPacksize());
    }
    return false;
}

bool Widget::handleHouseVideo(TcpSession* session, Pack& rcvPack){
    //房间号
    int houseID;
    memcpy(&houseID, (char*)&rcvPack+12, 4);
    //根据房间号查找所有的session
    if(!houseMap.contains(houseID))
        return false;
    //
    QMap<QString, TcpSession*> & house=houseMap.find(houseID).value();
    for(auto & e : house){
        if(e != session){
            e->send((char*)&rcvPack, rcvPack.getPacksize());
        }
    }
    return false;
}

bool Widget::handleHouseCamera(TcpSession *session, Pack &rcvPack)
{
    return handleHouseVideo(session, rcvPack);
}


